﻿using System;
using System.Collections.Generic;
using System.Text;

namespace eKanBan.Model
{
    [Serializable]
    class KanbanBoard
    {
        private List<Phase> phases;
        private BasePhase beginPhase, endPhase;
        private List<Member> members;
        private List<Feature> features;
        private List<Task> tasks;

        public BasePhase BeginPhase { get { return beginPhase; } }
        public BasePhase EndPhase { get { return endPhase; } }

        public int PhaseCount { get { return phases.Count; } }


        public IList<Feature> Features { get { return features.AsReadOnly(); } }
        public IList<Member> Members { get { return members.AsReadOnly(); } }
        public IList<Task> Tasks { get { return tasks.AsReadOnly(); } }
        public IList<Phase> Phases { get { return phases.AsReadOnly(); } }

        public KanbanBoard(string beginPhaseName, string endPhaseName)
        {
            phases = new List<Phase>();
            BasePhase beginPhase = new BasePhase(beginPhaseName);
            BasePhase endPhase = new BasePhase(endPhaseName);
            this.beginPhase = (beginPhase);
            this.endPhase = (endPhase);
            members = new List<Member>();
            features = new List<Feature>();
            tasks = new List<Task>();
        }

        public Phase DefineNewPhase(string name, string definitionOfDone, int maxOfFeature)
        {
            Phase newPhase = new Phase(name, definitionOfDone, maxOfFeature);
            phases.Add(newPhase);

            return newPhase;
        }

        public void UpdatePhase(Phase phaseToUpdate, string name, string definitionOfDone, int maxOfFeature)
        {
            phaseToUpdate.Name = name;
            phaseToUpdate.DefinitionOfDone = definitionOfDone;
            phaseToUpdate.MaxFeature = maxOfFeature;
        }

        public void AddNextToPhase(BasePhase phaseToAdd, BasePhase phaseNext)
        {
            phaseToAdd.AddNextPhase(phaseNext);
        }

        public void RemoveNextToPhase(BasePhase phaseToRemove, BasePhase phaseNext)
        {
            phaseToRemove.RemoveNextPhase(phaseNext);
        }
        public bool CanRemove(Phase phase)
        {
            return phase.NextPhaseCount == 0 && phase.FeatureCount == 0;
        }

        public void RemovePhase(Phase phase)
        {
            if (CanRemove(phase))
                phases.Remove(phase);
        }

        public BasePhase GetPhaseById(int id)
        {
            if (id == 0)
                return beginPhase;
            if (id == phases.Count + 1)
                return endPhase;
            if (id < 0 || id > phases.Count + 1)
            {
                return null;
            }
            return phases[id - 1];
        }

        public void DefineAMove(BasePhase phaseFrom, BasePhase phaseTo)
        {
            phaseFrom.AddNextPhase(phaseTo);
        }

        public void RemoveAMove(BasePhase phaseFrom, BasePhase phaseTo)
        {
            phaseFrom.RemoveNextPhase(phaseTo);
        }

        public void AddFeatureToPhase(BasePhase phase, Feature feature)
        {
            phase.Add(feature);
        }

        public void RemoveFeatureFromPhase(BasePhase phase, Feature feature)
        {
            phase.Remove(feature);
        }

        public Feature DefineNewFeature(string description, int priority)
        {
            Feature newFeature = new Feature(BeginPhase, description, priority);
            features.Add(newFeature);
            return newFeature;
        }

        public void UpdateFeature(Feature feature, string description, int priority, bool done)
        {
            feature.Description = description;
            feature.Priority = priority;
            feature.Done = done;
        }

        public void RemoveFeature(Feature feature)
        {
            if (feature.CurrentPhase.CanRemove(feature))
            {
                feature.CurrentPhase.Remove(feature);
                features.Remove(feature);
            }
        }

        public void MoveFeature(Feature feature, BasePhase toPhase)
        {
            if (feature.CanMove(toPhase))
            {
                feature.Move(toPhase);
            }
        }

        public Task DefineNewTask(Feature feature, string description, int priority, Task.StatusType status, DateTime deadLine)
        {
            Task task = new Task(feature);
            task.Description = description;
            task.Priority = priority;
            task.Status = status;
            task.DeadLine = deadLine;
            feature.AddNewTask(task);
            tasks.Add(task);
            return task;
        }

        public void UpdateTask(Task task, string description, int priority,
            Task.StatusType status, DateTime deadLine, DateTime startTime, DateTime finishTime)
        {
            if (startTime >= finishTime)
            {
                throw new Exception("cannot set startTime >= finishTime");
            }
            task.Description = description;
            task.Priority = priority;
            task.Status = status;
            task.DeadLine = deadLine;
            task.StartTime = startTime;
            task.FinishTime = finishTime;
        }

        public void RemoveTask(Task task)
        {
            foreach (Member item in task)
            {
                item.OutOfTask(task);
            }
            task.Feature.RemoveTask(task);
            tasks.Remove(task);
        }

        public Member DefineNewMember(string name, bool sex, string comment)
        {
            Member newMember = new Member();
            newMember.Name = name;
            newMember.Sex = sex;
            newMember.Comment = comment;
            members.Add(newMember);

            return newMember;
        }

        public void UpdateMember(Member member, string name, bool sex, string comment, Task newTask)
        {
            member.Name = name;
            member.Sex = sex;
            member.Comment = comment;
            member.Take(newTask);
        }

        public void Remove(Member member)
        {
            if (member.IsDoing)
            {
                member.Doing.RemoveMember(member);
            }
            if (members.Contains(member))
            {
                members.Remove(member);
            }
        }
        public override string ToString()
        {
            return ViewBoard();
        }

        public string ViewBoard()
        {
            string str = "#0\tBeginPhase: " + beginPhase.Name;
            int count = 0;
            foreach (Phase item in phases)
            {
                str += String.Format("\n#{0}\tPhase {0}: {1}", ++count, item.Name);
            }

            str += String.Format("\n#{0}\tEndPhase: ", count + 1) + endPhase.Name;
            return str;
        }

        public string ViewPhase(BasePhase phase)
        {
            return phase.ToString();
        }

        public string ViewFeature(Feature feature)
        {
            return feature.ToString();
        }

        public string ViewTask(Task task)
        {
            return task.ToString();
        }

        public string ViewMember(Member member)
        {
            return member.ToString();
        }

        public static void Save(string fileName, KanbanBoard kanbanBoard)
        {
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter =
                new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

            using (System.IO.FileStream file = new System.IO.FileStream(fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write))
                formatter.Serialize(file, kanbanBoard);
        }

        public static KanbanBoard Load(string fileName)
        {
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter =
                new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

            using (System.IO.FileStream file = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                return (KanbanBoard)formatter.Deserialize(file);
        }
    }
}
